/******************************************************************************
 * $Id: text.c,v 1.185 2004/09/22 01:14:23 jimb Exp $
 * @file
 * @author  Jim Brooks http://www.jimbrooks.org
 * @brief   Functions for the text widget.
 ******************************************************************************/

#include "common.h"
#include "text.h"

#include "module.h"
#include "file.h"
#include "widgets.h"

#include "toolbar.h"

#include "statusbar.h"
#include "focusbar.h"
#include "tags.h"
#include "treefunc.h"
#include "handlers.h"

#include "language.h"
#include "popup.h"

#include "encoding.h"
#include "module_opened.h"
#include "handlers_module_opened.h"

#include <gtksourceview/gtksourcelanguage.h>
#include <gtksourceview/gtksourcelanguagemanager.h>

#include <libgnomevfs/gnome-vfs-init.h>
#include <libgnomevfs/gnome-vfs-mime-utils.h>
#include <libgnomevfs/gnome-vfs-utils.h>

/******************************************************************************
 * Creates a text widget and ancillary widgets (vbox/scrollbar/focusbar/etc).
 * Connects to keypress/focus handlers.
 ******************************************************************************/
void
CreateTextWidgetEtAl( GtkWidget**  ppWidgetText,	/*OUT*/
                      GtkWidget**  ppWidgetVbox,	/*OUT*/
                      GtkWidget**  ppWidgetTable,	/*OUT*/
                      GtkWidget**  ppWidgetVscroll,	/*OUT*/
                      GtkWidget**  ppWidgetHscroll,	/*OUT*/
                      focusbar_t** ppFocusbar		/*OUT*/ )
{
   GtkSourceLanguageManager *LangManager=NULL;
   LangManager = gtk_source_language_manager_new();

  /*
   * Create a text widget.
   */
  *ppWidgetText = gtk_source_view_new();
  if ( NULL == *ppWidgetText )
  {
	  PrintStatusbar("Ohh, Create GtkSourceView fail.\n");
	  return;
  }
   GtkTextBuffer * pSrcBuf=gtk_text_view_get_buffer(
									GTK_TEXT_VIEW(*ppWidgetText));
   if ( NULL == pSrcBuf )
   {
   	g_print("Create SourceBuffer err. May be less of memory.\n");
   	*ppWidgetText = NULL;
   	return;
   }
   // check if AUX text_view, the disable editable
   if ( ppWidgetText == &pWidgetTextAux )
   {
	   gtk_text_view_set_editable(GTK_TEXT_VIEW(*ppWidgetText), FALSE);
	   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(*ppWidgetText), FALSE);
   }
   else
   {
	   gtk_text_view_set_editable(GTK_TEXT_VIEW(*ppWidgetText), TRUE);
   }
   g_object_ref(LangManager);
   g_object_set_data_full(G_OBJECT(pSrcBuf),
   				    "languages-manager",
   				    LangManager,
   				    (GDestroyNotify)g_object_unref);
   g_object_unref(LangManager);


   /**** added any GtkSourceBuffer's attribute ****/
   gtk_source_view_set_highlight_current_line(GTK_SOURCE_VIEW(*ppWidgetText), bool_highlight_cur_line);
   gtk_source_view_set_auto_indent(GTK_SOURCE_VIEW(*ppWidgetText), bool_auto_indent);
   gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(*ppWidgetText), bool_show_line_numbers);
   gtk_source_view_set_show_right_margin(GTK_SOURCE_VIEW(*ppWidgetText), bool_show_margin);
   gtk_source_view_set_smart_home_end(GTK_SOURCE_VIEW(*ppWidgetText), bool_smart_home_end);
   gtk_source_view_set_insert_spaces_instead_of_tabs(GTK_SOURCE_VIEW(*ppWidgetText), bool_space_instead_of_tabs);
   
   //gtk_source_view_set_tabs_width(GTK_SOURCE_VIEW(*ppWidgetText), tabWidth);
  /*
   * Change the tab width only if specified by the command line (or GNOME config).
   * The default tab width is usually best.
   */
   ApplyTabWidth( *ppWidgetText, tabWidth );


//   gtk_source_view_set_show_line_markers(GTK_SOURCE_VIEW(*ppWidgetText), TRUE);
   /***********************************************/

#if 0 //lv
   gtk_text_set_editable( GTK_TEXT(*ppWidgetText),
#if 1
/* ### KLUDGE: To show the cursor, text widget must be set editable. ### */
                          TRUE );
#else
                          FALSE );
#endif

#endif

  if ( ppWidgetText == &pWidgetTextMain )
  {
  /*
   * Install a keyboard event handler.
   */
   g_signal_connect( G_OBJECT(*ppWidgetText),
                       "key-press-event",
                       G_CALLBACK(HandlerKeyPressText),
                       NULL );
   g_signal_connect( G_OBJECT(*ppWidgetText),
                       "key-release-event",
                       G_CALLBACK(HandlerKeyRelease),
                       NULL );

   // lv added
   g_signal_connect( G_OBJECT(*ppWidgetText),
						"button-press-event",
						G_CALLBACK(cb_button_press_event),
						NULL);
   /*
   g_signal_connect( G_OBJECT(*ppWidgetText),
						"cut/copy/paste-clipboard",
						G_CALLBACK(cb_xxx
	*/
   g_signal_connect( G_OBJECT(pSrcBuf),
						"modified-changed",
						G_CALLBACK(cb_modified_changed),
						*ppWidgetText);

   g_signal_connect( G_OBJECT(pSrcBuf),
						"changed",
						G_CALLBACK(cb_changed),
						*ppWidgetText);

   gtk_widget_add_events( *ppWidgetText,
                          GDK_KEY_RELEASE_MASK );	/* to enable key release events */

  }// if ( ppWidgetText == &pWidgetTextMain )

  /*
   * Install the mouse event handler for mousewheel
   */
#if 0//lv
   g_signal_connect( G_OBJECT(*ppWidgetText),
                       "button-press-event",
                       G_CALLBACK(HandlerMouseButton),
                       GTK_TEXT(*ppWidgetText)->vadj );
#endif
  /*
   * Install handlers for when text widget gains/loses focus.
   */
   InstallFocusHandlers( *ppWidgetText );

#if 1 //lv
/* ## Scrollbars do not work using scrolled windows. ## */
   GtkWidget *textScrolled;
   textScrolled = gtk_scrolled_window_new( NULL, NULL );
   gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(textScrolled),
                                   GTK_POLICY_AUTOMATIC,
                                   GTK_POLICY_ALWAYS);
   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(textScrolled),
									GTK_SHADOW_IN);

	gtk_container_add(GTK_CONTAINER(textScrolled), *ppWidgetText);
#endif 

  /*
   * Change its color.
   */
   ChangeStyleColorText( *ppWidgetText,
                         &colorTextWidgetTextFg,
                         &colorTextWidgetTextBg );
   ChangeStyleColorSel( *ppWidgetText,
                        &colorTextWidgetSelFg,
                        &colorTextWidgetSelBg );

  /*
   * Change its font.
   */
   ChangeStyleFont( *ppWidgetText,
                    fontNameText );

  /*
   * Create a "focus bar" to be placed above the text widget.
   */
#if 0
   *ppFocusbar = FocusbarCreate( "8x13" );
#else
   *ppFocusbar = FocusbarCreate( "Sans");//"6x12" );
#endif

  /*
   * In focusbar, handle GDK mouse button events.
   * Since a GDK event, not a GTK+ signal, will be handled,
   * calling gtk_widget_add_events() is necessary.
   */
   g_signal_connect( G_OBJECT((*ppFocusbar)->pWidget),
                       "button-press-event",
                       G_CALLBACK(HandlerFocusbarButtonEvent),
                       *ppFocusbar );
   gtk_widget_add_events( (*ppFocusbar)->pWidget,
                          GDK_BUTTON_PRESS_MASK );

  /*
   * Create a 2-slot vbox to hold text widget and its "focus bar".
   */
   *ppWidgetVbox = gtk_vbox_new( FALSE, 0 );
   gtk_box_pack_start( GTK_BOX(*ppWidgetVbox),		/* pack focusbar */
                       (*ppFocusbar)->pWidget,
                       FALSE, FALSE, 2 );			/* padding to avoid clipping paned slider */

   gtk_box_pack_end( GTK_BOX(*ppWidgetVbox),		/* pack table containing text widget/scrollbars */
                    // *ppWidgetTable,
					 textScrolled,
                     TRUE, TRUE, 0 );

   return;
}

/*****************************************************************************
 * Connect to handlers for a text widget (main or aux).
 *****************************************************************************/
void
ConnectHandlersForTextWidget( GtkWidget* pWidget )
{
#if 0
/* HANDLER WILL NEVER BE CALLED */
/* LEFT HERE AS INFO */
   gtk_signal_connect( GTK_OBJECT(pWidget),
                       "move_cursor",
                       (GtkSignalFunc)HandlerLineNum,
                       NULL );
#endif
   g_signal_connect( G_OBJECT(pWidget),
                       "button-press-event",
                       G_CALLBACK(HandlerLineNum),
                       NULL );
   if ( pWidget != pWidgetTextAux )
   {
	   g_signal_connect( G_OBJECT(pWidget),
                       "key-press-event",
                       G_CALLBACK(HandlerLineNum),
                       NULL );

	   GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidget));
		g_assert(NULL!=pBuf);
	   g_signal_connect( G_OBJECT(pBuf),
	                       "changed",
	                       G_CALLBACK(HandlerLineNum),
	                       NULL );
   }
   return;
}

/*****************************************************************************
 * Apply a tab width to a text widget.
 * Does NOT redraw the text widget.
 * Does NOT affect global vars.
 * @param   pWidget
 *          Must be a text widget.
 * @param   width
 *****************************************************************************/
void
ApplyTabWidth( GtkWidget* pWidget,
               int        width )
{
  /*
   * Has text widget been created?
   */
   if ( pWidget == NULL ) return;

  /*
   * width can be 0 (which selects GTK+ default).
   */
   g_assert( width >= 0  &&  width < 30 );
   gtk_source_view_set_tab_width(GTK_SOURCE_VIEW(pWidget), width);
   return;
}

/******************************************************************************
 * Load the text widget with a module (ie source file).
 * Also moves the cursor to the specified line.
 * Frees buffer holding previous module depending on conserveMemory.
 * @param   pModuleName
 *          Name of module (typically a file name).
 * @param   cursor
 *          Which line to move the cursor to.
 * @param   row
 *          The associated list widget row (use ROW_NONE if N/A).
 * @param   ActiveModule() (OUT/GLOBAL)
 *          This function will cause ActiveModule() to return
 *          the hash item of the module loaded by this function.
 * @param   pHashModules (GLOBAL)
 *          The module hash will be updated if the text widget is copied out
 *          (into a new buffer) before being loaded with another module.
 * @param   reload
 *          If TRUE then the text widget will be loaded, even if it already
 *          contains the requested module.
 * @return  FUNC_OK or FUNC_FAIL
 ******************************************************************************/
FUNC_X
LoadTextWidgetWithModule( gchar*   pModuleName,
                          gint	   cursor,
                          gint     row,
                          gboolean reload )
{
   char*		pBuffer = NULL;
   guint		size;
   const char*	pLine = NULL;
#if 0 //not-need-again
  /*
   * Clear statusbar if it still says "buffer too large to hilite"
   * while another module is being loaded.
   */
   if ( GetStatusbarMsg() != NULL )
   {
      if ( strstr( GetStatusbarMsg(), BUFFER_TOO_LARGE_TO_HILITE_STR ) )
         ClearStatusbar();
   }
#endif
  /*
   * Load the text widget only if it does not already contain the specified module
   * (or a reload is requested).
   */
   if ( reload  ||  CompareStrings( pModuleName, ActiveModuleName() ) )
   {

#if 0
      CopyOutTextWidget();
#endif

	if ( pWidgetTextActive == pWidgetTextMain )
	{
		//Save changes to `file'?
		// No, Cancel, Yes
		
       if ( IsMainModuleModified() )
	   {
		  //Save changes to `file'?
		  // No, Cancel, Yes
		  gint result = AskSaveOrNot();
		  if  ( result == GTK_RESPONSE_YES || 
		        result == GTK_RESPONSE_NO )
		  {
		       SetMainModuleModified(FALSE);
		       // do nothing
		   }
		   else
		        return FUNC_OK;
	   }// if 2
	   else if ( !GetModuleTextMain() ) //
	   {
	        // if new file and have contents
	        if (IsMainTextViewHaveContents())
	        {
                gint result = 
	                RunDialogMessageQuestion(pWidgetMain,
	                                 _("Save changes to file?"));

	            switch (result )
	            {
	                case GTK_RESPONSE_YES:
	                    // save file fail
                         if (SaveFileAs(NULL) == FALSE )
                            return FUNC_OK;
                         break;
	                case GTK_RESPONSE_NO:
                         ;
                         break;
	                default:
	                     return FUNC_OK;
	                     break;
                }

	        }
	   }


	 }// if()
			
      RecordActiveModule( NULL );
      /*
      * Discard previous text buffer.
      */
      
      // 虽然程序起动时'扫描函数'将 buffer 改变了，但我随之又对其重新加载，因此
      // 这里可以不必再加载也能保证原文的内容不变。(但会增加内存用量，所有文件均被加载入内存)
      FreeModuleBuffer( ActiveModuleName() ); // 这里应该检查（或者程序应该监视有没有文件被外部程序修改

     /*
      * Obtain a pointer to the buffer that holds the specified module
      * (loading it from a file if necessary).
      */
      pBuffer = ModuleBuffer( pModuleName, &size/*OUT*/ );
      if ( ! pBuffer ) return FUNC_FAIL;

	  
     /*
      * Record the name of the module loaded into the text widget.
      */
      RecordActiveModuleName( pModuleName );

     /*
      * Remember hash item (to speed functree).
      */
      RecordActiveModule( pModuleLoaded ); /* pModuleLoaded assigned by ModuleBuffer() above */

     /*
      * Actually load the text widget with the module buffer.
      */
      LoadTextWidgetWithBuffer( pBuffer,
                                size,
                                WhichKindOfSrcFile( pModuleName ) );
      
      /*
       * if reload TextMain, then refresh the function list
       */
      if ( pWidgetTextActive == pWidgetTextMain )
          RefreshToolbarFuncList(ActiveModule());
   }
   else
   {
     /*
      * Text widget already contains the requested module.
      * Just get a pointer to its buffer (for the following).
      */
      pBuffer = ModuleBuffer( pModuleName, &size/*OUT*/ );
      if ( ! pBuffer ) return FUNC_FAIL;
   }

  /*
   * Move the cursor to the specified line.
   */
   pLine = ToLine( pBuffer, cursor, MAX_LINE_LEN );
   if ( ! pLine )
   {
     /*
      * Unable to find this line.
      */
      PrintStatusbarForRow( _("This line is beyond the end.  Tag information probably is stale."),
                            row );
      return FUNC_FAIL;
   }
   ScrollTextWidgetToLine( cursor,
                           SCROLL_TEXT_VERT_FRAC );

  /*
   * Update the text widget's focusbar label, to show which module is being shown.
   */
   FocusbarLabelSet( pFocusbarTextActive, pModuleName );

   return FUNC_OK;
}

/******************************************************************************
 * Load the text widget with a memory buffer (overwriting current contents).
 * This function is generic, therefore, it is ignorant of what the buffer is.
 * @param   pBuffer
 * @param   bufferSize
 * @param   srcFileKind
 *          One of SRC_FILE_*
 ******************************************************************************/
void
LoadTextWidgetWithBuffer( gpointer pBuffer,
                          gint     bufferSize,
                          int      srcFileKind )
{


   GtkTextBuffer *pTmpBuf;
   pTmpBuf=gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));

   gtk_widget_set_sensitive(GTK_WIDGET(pWidgetTextActive), FALSE);
  /*
   * Clear the text widget.
   */
   ClearTextWidget();

  /*
   * Insert the buffer into the text widget.
   */

   gtk_source_buffer_begin_not_undoable_action(GTK_SOURCE_BUFFER(pTmpBuf));
//   GtkTextIter    iter;
//   gtk_text_buffer_get_end_iter(pTmpBuf, &iter);
   // 往text控件中插入文件中的内容
//   gtk_text_buffer_insert(pTmpBuf, &iter, pBuffer, bufferSize);//lv

   gtk_text_buffer_set_text(pTmpBuf,pBuffer, bufferSize);

   if ( hilite )//&& srcFileKind != SRC_FILE_OTHER )
	   SetBufferHighlight( GTK_SOURCE_BUFFER(pTmpBuf), ActiveModuleName());

   gtk_source_buffer_end_not_undoable_action(GTK_SOURCE_BUFFER(pTmpBuf));

   gtk_widget_set_sensitive(GTK_WIDGET(pWidgetTextActive), TRUE);
   gtk_text_buffer_set_modified(GTK_TEXT_BUFFER(pTmpBuf), FALSE);

   return;
}

gboolean
cb_RefreshAuxTextWidget( gpointer data )
{
//    RefreshAuxTextWidget();
    
   if ( IsAnyMajorFunctionBusy(TRUE) ) return TRUE;

   gboolean auxKeyPressedSaved = auxKeyPressed;
    
   SetSensitivityWhileBusy( FALSE );
   auxKeyPressed = TRUE;
   GotoTagUnderCursor();
   SetSensitivityWhileBusy( TRUE );
    
   auxKeyPressed = auxKeyPressedSaved;

    /*
     * set idle_func's id = 0
     */
    if ( NULL != data )
        *(int*)data = 0;
    
    /* glib:
     * It should return FALSE, if the source
     * should be removed.
     */
    return FALSE;
}

/******************************************************************************
 * Only reload the AuxTextWidget's contents and set the module for Aux
 ******************************************************************************/
void
RefreshAuxTextWidget()
{
    if ( IsAnyMajorFunctionBusy(FALSE) )
        return;
    // find tag
    GString * pWord = WordAtCursor();
    if ( NULL == pWord ) goto quit;
    
    /*
     * A bug here, cannot find all tags ,only first !!
     */
    tag_t * pTag = FindTag( pWord->str, NULL );
    if ( NULL == pTag ) return;
    
    // set env
    module_t * pModule = FindModule( pTag->pModuleName );
    if ( NULL == pModule ) return;

    /*
     * 
     */
/*    GtkWidget* pWidgetTextActiveSaved = pWidgetTextActive;
    SetActiveTextWidget( pWidgetTextAux );
    
    module_t * pModuleLoadedSaved = ActiveModule ();
    
        RecordActiveModule( pModule ); 
        pModuleLoaded = pModule;
        RecordActiveModuleName( pModule->pModuleName );
 */   
    if ( GetModuleTextAux() != pModule )
    {
        SetModuleTextAux( pModule );
        // refresh aux
        RefreshTextWidget( pWidgetTextAux, pModule );
    }
    // highlight tag
    
GtkWidget* pWidgetTextActiveSaved = pWidgetTextActive;
HandlerFocusIn(pWidgetTextAux,NULL,NULL);// SetActiveTextWidget( pWidgetTextAux );

//ScrollTextWidgetToLine(pTag->lineInt, 0.24);//HighlightTag( pTag, -1 );

HighlightTag( pTag, -1 );

HandlerFocusIn(pWidgetTextActiveSaved,NULL,NULL);//SetActiveTextWidget( pWidgetTextActiveSaved );

 /*
    pModuleLoaded = pModuleLoadedSaved;
    RecordActiveModule( pModuleLoadedSaved );
    RecordActiveModuleName( pModuleLoadedSaved == NULL?
                NULL: pModuleLoadedSaved->pModuleName );
    
    SetActiveTextWidget( pWidgetTextActiveSaved );
  */  
quit:
    if ( pWord )
        g_string_free( pWord, TRUE );
    return;
}

/******************************************************************************
 * Reload the pModule's contents into pWidgetText
 * Don't scroll and don't change the focus and the modified flag
 ******************************************************************************/
void
RefreshTextWidget( GtkWidget * pWidgetText,
                   module_t  * pModule )
{
    if ( NULL == pWidgetText ||
         NULL == pModule )
       return;

    GtkTextBuffer * pTextBuf;
    pTextBuf = gtk_text_view_get_buffer( GTK_TEXT_VIEW(pWidgetText) );
    if ( NULL == pTextBuf )
        return;
        
   gtk_widget_set_sensitive( GTK_WIDGET(pWidgetText), FALSE );
   
   gtk_source_buffer_begin_not_undoable_action(GTK_SOURCE_BUFFER(pTextBuf));

   gtk_text_buffer_set_text( pTextBuf, "" ,0 ); // -1 //lv

   if ( NULL != pModule->pModuleBuffer )
      gtk_text_buffer_set_text( pTextBuf,
                                pModule->pModuleBuffer,
                                pModule->moduleSize );

   if ( hilite )//&& srcFileKind != SRC_FILE_OTHER )
	   SetBufferHighlight( GTK_SOURCE_BUFFER( pTextBuf ),
	                       pModule->pModuleName );
	   
   gtk_source_buffer_end_not_undoable_action( GTK_SOURCE_BUFFER(pTextBuf) );
   
   gtk_widget_set_sensitive(GTK_WIDGET(pWidgetText), TRUE);   

   return;
}

/*****************************************************************************
 * Get the word at CURRENT cursor in the CURRENT text widget.
 *****************************************************************************/
GString*
WordAtCursor( void )
{
   GString*		pGstr		= NULL;
   gchar*		pText		= NULL;
   guint		textLen;
   guint		cursorIdx;

  /*
   * Prevent nesting major functions.
   */
   if ( IsAnyMajorFunctionBusy(TRUE) ) return NULL;
   if ( IsTextWidgetEmpty() ) return NULL;
  /*
   * Obtain contents of text widget.
   */
   pText = ActiveTextWidgetContents( &textLen );
   if ( pText == NULL  ||  textLen > MAX_TEXT_BUFFER_LEN ) goto error;

  /*
   * Get index of cursor.
   */
   cursorIdx = OffsetAtCursor();//TextCursorIdx();
   if ( cursorIdx < 0  ||  cursorIdx > textLen ) goto error;

  /*
   * Scoop word at cursor.
   * Return if cursor is not over a word.
   */

   pGstr = ScoopWordAtCursor( MAX_WORD_LEN,
                              pText,
                              textLen,
                              cursorIdx );
   if ( pGstr == NULL ) goto quit;		/* benignly quit if cursor isn't on a word */

   /* 
    * Successful 
    */
   return pGstr;
   
  /*
   * Free mem, return.
   */
quit:
   if ( pGstr ) g_string_free( pGstr, TRUE );
       return NULL;

  /* 
   * Oops.
   */
error:
   WarningMalfunction();
   return NULL;
}

/******************************************************************************
 * Clear the text widget.
 * @pre The caller should first freeze the text widget.
 ******************************************************************************/
void
ClearTextWidget( void )
{

   GtkTextBuffer * pSrcBuf = gtk_text_view_get_buffer(
						       GTK_TEXT_VIEW(pWidgetTextActive));
   gtk_widget_set_sensitive( GTK_WIDGET(pWidgetTextActive), FALSE );
   
   gtk_source_buffer_begin_not_undoable_action(GTK_SOURCE_BUFFER(pSrcBuf));

   gtk_text_buffer_set_text( GTK_TEXT_BUFFER(pSrcBuf),"",0 ); // -1 //lv

   gtk_source_buffer_end_not_undoable_action(GTK_SOURCE_BUFFER(pSrcBuf));
   
   gtk_widget_set_sensitive( GTK_WIDGET(pWidgetTextActive), TRUE );   
   return;
}

/*****************************************************************************
 * Redraw the text widgets (unless empty).
 *****************************************************************************/
void
RedrawBothTextWidgets( void )
{
   GtkWidget*	pWidgetTextActiveSaved;

  /*
   * Redraw both the main and aux text widgets,
   * afterwards restore the active one.
   */
   pWidgetTextActiveSaved = pWidgetTextActive;

      SetActiveTextWidget( pWidgetTextMain );	/* redraw main text widget */
      RedrawActiveTextWidget();

      SetActiveTextWidget( pWidgetTextAux );	/* redraw aux text widget */
      RedrawActiveTextWidget();

   SetActiveTextWidget( pWidgetTextActiveSaved );

   return;
}
/** Redraw active text widget. */
void
RedrawActiveTextWidget( void )
{
   int			line;
   gchar		moduleThatWasLoaded[MAX_FILENAME_LEN];

   if ( ! IsTextWidgetEmpty() )
   {
      line = LineAtCursor();
      strcpy( moduleThatWasLoaded, ActiveModuleName() );
      LoadTextWidgetWithModule( moduleThatWasLoaded,
                                line,
                                ROW_NONE,
                                DO_RELOAD );
   }
   return;
}

/******************************************************************************
 * @return True if the text widget is empty (or isn't created yet).
 ******************************************************************************/
int
IsTextWidgetEmpty( void )
{
   if ( pWidgetTextActive == NULL ) return TRUE;
   if ( ActiveModuleName()[0] == '\0' ) return TRUE; /* if a char is typed into a empty text widget */
//   return gtk_text_get_length( GTK_TEXT(pWidgetTextActive) ) == 0;
   GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
   return gtk_text_buffer_get_char_count(GTK_TEXT_BUFFER(pBuf)) == 0;
}



/*****
 * get the offset of the buffer at the cursor.
 * Note: Encoding: utf-8
 */
gint
OffsetAtCursor(void)
{
	gchar * pText = NULL;
	if ( pWidgetTextActive == NULL )
		return -1;
	pText = ActiveTextWidgetContents(NULL);
	if ( NULL == pText )
		return -1;

	GtkTextBuffer * pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
	GtkTextIter iter;
	gtk_text_buffer_get_iter_at_mark(pBuf,
									 &iter,
									 gtk_text_buffer_get_insert(pBuf));
	return gtk_text_iter_get_offset(&iter);
}

/******************************************************************************
 * @return The line where the cursor is at in the text widget.
 * Returns -1 upon failure.
 * Note: In textview the firstline is 0, but LineATCursor() is 1
 ******************************************************************************/
int
LineAtCursor( void )
{
   gchar*	pText = NULL;

   if ( pWidgetTextActive == NULL ) return -1;

   pText = ActiveTextWidgetContents( NULL );
   if ( pText == NULL ) return -1;

   GtkTextBuffer *pSrcBuf=gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
   GtkTextIter iter;
   gtk_text_buffer_get_iter_at_mark(pSrcBuf,&iter,
					gtk_text_buffer_get_insert(pSrcBuf));
  return  gtk_text_iter_get_line(&iter)+1;
}
/*
 * Scroll the text view to the place of the cursor.
 * @param	pView
 *			which buffer
 * @param   within_margin ( must <0.5)
 *			0.5=center, 0=lefttop 1.0=rightbutton
 * @author  lvjinhua
 */
void
ScrollToCursor(GtkTextView *pView, gdouble within_margin)
{
	g_return_if_fail(NULL != pView);
	GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pView));
	gtk_text_view_scroll_to_mark(pView,
								gtk_text_buffer_get_insert(pBuf),
								within_margin,
								FALSE,
								0,0);

// Cannot work correctly
/*	gtk_text_buffer_get_iter_at_line(pBuf, &iter, LineAtCursor() -1);
	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(pView),
								 &iter,
								 within_margin,
								 FALSE,
								 0,
								 0);
*/
	return;
}

/******************************************************************************
 * Scroll the text widget so that a line is visible.
 * Also moves the cursor to the line.
 * @param   line
 *          Which line # to scroll to.
 * @param   vertFrac
 *          {0.0 ... 1.0} in which 0.0 scrolls line to top, 1.0 to bottom.
 * @param   pWidgetTextActive (GLOBAL)
 *
 * @note    This code was stolen from GXedit ;-)
 * @verbatim
 * - - - - - OLD JUNK LEFT AS INFO - - - - - -
 * Skip setting an scroll increment (ie adjustment) value if the text widget
 * has only one page, otherwise, the unnecessary scroll is annoying to user.
 *
 * One page is indicated if upper == pageinc (multiple pages if upper > paginc).
 * if (   GTK_ADJUSTMENT(GTK_TEXT(pWidgetTextActive)->vadj)->upper
 *      > GTK_ADJUSTMENT(GTK_TEXT(pWidgetTextActive)->vadj)->page_increment )
 * { <if text widget is more than a page> }
 *
 * A 0.5 page_increment is what causes the scrolled-to-line
 * to be positioned at the middle of the text widget.
 * - - - - - - - - - - - - - - - - - - - - - - -
 * @endverbatim
 ******************************************************************************/
void
ScrollTextWidgetToLine( gint    line, // from 1 .. END_Line
                        gfloat vertFrac )
{

   GtkTextBuffer *  pBuf		= NULL;
g_return_if_fail( pWidgetTextActive );

g_return_if_fail( line >= 0  &&  line <= MAX_LINES );

   scrolling = TRUE;	/* reported if SIGSEGV happens */
   scrollLine = line;	/* reported if SIGSEGV happens */

   pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
  /*
   * line == 0 causes gtk_text_insert() to erratically assert.
   */
   if ( line <= 0 ) line = 1;


   GtkTextIter iter;
   // yes, line -1 
   gtk_text_buffer_get_iter_at_line(pBuf, &iter, line -1);
   gtk_text_buffer_place_cursor(pBuf, &iter);
   ScrollToCursor(GTK_TEXT_VIEW(pWidgetTextActive), vertFrac);

   scrolling = FALSE;
   return ;
}

/*
 * set the adjustment value of a widget
 * The value will be forced to be within the range {lower .. higher}
 */
void
SetAdjustmentValue(GtkAdjustment *pAdj,
				   gfloat adjust)
{
   gfloat lower;
   gfloat upper;

g_return_if_fail( pAdj );

   lower = pAdj->lower;
   upper = pAdj->upper;

   if      ( adjust < lower )
      adjust = lower;
   else if ( adjust > upper )
      adjust = upper;

   gtk_adjustment_set_value( pAdj, adjust );

   return;
}

/******************************************************************************
 * Emulates emacs Ctrl+L.
 ******************************************************************************/
void
RecenterTextWidget( void )
{
  /*
   * ScrollTextWidgetToLine() will do so by passing the current line #.
   */
   gint Line=LineAtCursor();
   if ( ! IsTextWidgetEmpty()  &&  Line > -1 )
   {
      ScrollTextWidgetToLine( Line,
                              0.25 );
   }
   return;
}

/*****************************************************************************
 * Create/show a dialog widget for jumping to a line # entered by user.
 * This is called when the user selects the menu item /Goto/Line.
 * @param   pWidgetDialogLine (OUT/GLOBAL)
 *          Pointer to the created dialog widget for /Goto/Line.
 *****************************************************************************/
#if 0
void
GotoLine( void )
{
   GtkWidget*	pWidgetHbox;
   GtkWidget*	pWidgetLabel;

  /*
   * Is the text widget empty?
   */
   if ( IsTextWidgetEmpty() )
   {
      PrintStatusbar( "Text widget is empty." );
      return;
   }

  /*
   * Create the dialog widget.
   */
   pWidgetDialogLine = gtk_dialog_new();

  /*
   * Install a key press event handler (to close dialog if ESC is pressed).
   */
   gtk_signal_connect( GTK_OBJECT(pWidgetDialogLine),
                       "key-press-event",
                       (GtkSignalFunc)HandlerKeyPressDialogLine,
                       NULL );

  /*
   * Title the dialog.
   */
   gtk_window_set_title( GTK_WINDOW(pWidgetDialogLine),
                         "Goto Line" );

  /*
   * Widen the border of the dialog.
   */
   gtk_container_border_width( GTK_CONTAINER(pWidgetDialogLine),
                               16 );

  /*
   * Create a hbox to hold the widgets of the dialog.
   */
   pWidgetHbox = gtk_hbox_new( FALSE, 8 );

  /*
   * Create/pack a label for the dialog.
   */
   pWidgetLabel = gtk_label_new( "Line: " );
   gtk_box_pack_start( GTK_BOX(pWidgetHbox),
                       pWidgetLabel,
                       TRUE, TRUE, 0 );

  /*
   * Create/pack the combo (text entry field) of the dialog.
   * Do not add pop-down strings if this is the first instance.
   * gtk_combo_disable_active() makes Enter key work as expected.
   */
   pWidgetComboLine = gtk_combo_new();
   gtk_combo_disable_activate( GTK_COMBO(pWidgetComboLine) );
   if ( pGlistLineStrings != NULL )
      gtk_combo_set_popdown_strings( GTK_COMBO(pWidgetComboLine),
                                     pGlistLineStrings );
   gtk_box_pack_start( GTK_BOX(pWidgetHbox),
                       pWidgetComboLine,
                       TRUE, TRUE, 0 );

  /*
   * Clear the text entry widget (it repeats the last item typed).
   */
   gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(pWidgetComboLine)->entry),
                       "" );

  /*
   * The final pack is to pack the hbox into the dialog's vbox.
   */
   gtk_box_pack_start( GTK_BOX(GTK_DIALOG(pWidgetDialogLine)->vbox),
                       pWidgetHbox,
                       TRUE, TRUE, 0 );

  /*
   * Connect to signals.
   */
   gtk_signal_connect( GTK_OBJECT(GTK_COMBO(pWidgetComboLine)->entry),
                       "activate",
                       GTK_SIGNAL_FUNC(HandlerActivateComboLine),
                       pWidgetDialogLine );

  /*
   * Position/show the dialog (and its sub-widgets).
   */
   PositionShowDialogAtMiddle( pWidgetDialogLine );

  /*
   * Grab the focus for the entry widget.
   */
   gtk_widget_grab_focus( GTK_WIDGET(GTK_COMBO(pWidgetComboLine)->entry) );

   return;
}
#endif
// cancel pWidgetDialogLine
void GotoLine()
{
	GtkWidget *dialog;
	GtkWidget *button;
	GtkWidget *table;
	GtkWidget *label;
	GtkWidget *spinner;
	GtkAdjustment *spinner_adj;
	GtkTextIter iter;
	gint num, max_num;
	GtkWidget *textview = pWidgetTextActive;
	GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
	
	gtk_text_buffer_get_iter_at_mark(textbuffer, &iter,
		gtk_text_buffer_get_insert(textbuffer));
	num = gtk_text_iter_get_line(&iter) + 1;
	gtk_text_buffer_get_end_iter(textbuffer, &iter);
	max_num = gtk_text_iter_get_line(&iter) + 1;
	
	dialog = gtk_dialog_new_with_buttons(_("Jump To"),
		GTK_WINDOW(gtk_widget_get_toplevel(textview)),
		GTK_DIALOG_DESTROY_WITH_PARENT,
		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		NULL);
	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
//	button = create_button_with_stock_image(_("_Jump"), GTK_STOCK_JUMP_TO);
	button = gtk_button_new_with_label(_("Jump"));

	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
	gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_OK);
	table = gtk_table_new(1, 2, FALSE);
	 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
	 gtk_container_set_border_width (GTK_CONTAINER(table), 8);
	 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, FALSE, FALSE, 0);
	label = gtk_label_new_with_mnemonic(_("Line number:"));
	spinner_adj = (GtkAdjustment *) gtk_adjustment_new(num, 1, max_num, 1, 1, 0);
	spinner = gtk_spin_button_new(spinner_adj, 1, 0);
	 gtk_entry_set_width_chars(GTK_ENTRY(spinner), 8);
	 gtk_label_set_mnemonic_widget(GTK_LABEL(label), spinner);
	 gtk_entry_set_activates_default(GTK_ENTRY(spinner), TRUE);
	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
	gtk_table_attach_defaults(GTK_TABLE(table), spinner, 1, 2, 0, 1);
	
	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
	gtk_widget_show_all(dialog);
	
	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
		//
		gchar * lineAsStr=gtk_editable_get_chars(GTK_EDITABLE(spinner),0,-1);
		int line = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner) ) ;
		if ( line >= 0 && line < MAX_LINES)
		{
			if ( HighlightLine( line ) )
				;   //ScrollTextWidgetToLine(line, SCROLL_TEXT_VERT_FRAC);
			else
			{
				gchar *msg = g_strdup_printf(_("Invalid line # `%s'"),lineAsStr);
				PrintStatusbar(msg);
				g_free(msg);
			}
		}
		else
		{
			gchar *msg = g_strdup_printf(_("Invalid line # `%s'"),lineAsStr);
			PrintStatusbar(msg);
			g_free(msg);
		}
		g_free(lineAsStr);

		/*
		gtk_text_buffer_get_iter_at_line(textbuffer, &iter,
			gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner)) - 1);
		gtk_text_buffer_place_cursor(textbuffer, &iter);
//		gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textview), &iter, 0.1, FALSE, 0.5, 0.5);
		ScrollToCursor(GTK_TEXT_VIEW(textview), 0.25);
		*/
	}
	
	gtk_widget_destroy (dialog);
}

/******************************************************************************
 * Highlight a line in the text widget.
 * @para   line  
           from 1 ... end
 * @return False if line is invalid.
 ******************************************************************************/
gboolean
HighlightLine( int line )
{
   GtkTextIter StartIter;
   GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));

   if ( line > gtk_text_buffer_get_line_count(pBuf))
       return FALSE;
   
   if ( line <= 0 )
    line =1;
   
   gtk_text_buffer_get_iter_at_line(pBuf, &StartIter, line -1);
   gint OffsetStart = gtk_text_iter_get_offset(&StartIter);
   gint OffsetEnd   = gtk_text_iter_get_chars_in_line(&StartIter) + OffsetStart;
//   gint OffsetEnd   = gtk_text_iter_get_bytes_in_line(&StartIter) + OffsetStart;
   SelectRegion(pBuf,
					  OffsetStart,
					  OffsetEnd); // Conside of UTF-8
   return TRUE;
}

/******************************************************************************
 * Highlight the first matching word in the current line of the text widget.
 * If unable to find a match, as a contigency, highlight the entire line.
 *
 * The GTK "point" must point to the first char of the current line,
 * as set by gtk_text_set_point().
 *
 * @param   pWord
 *          Pointer to word (or regex) to highlight.
 * @param   caseSens
 * @param   searchMethod
 *          SEARCH_NORMAL or SEARCH_REGEX
 * @param   tryConstructorMatch
 *          If true, search for a second match on the same line.
 *          This is to hilite the function name instead of the class name
 *          for C++ constructors.
 ******************************************************************************/
void


HighlightWordInCurrentLine( const char* pWord,
                            gboolean    caseSens,
                            int         searchMethod,
                            gboolean    tryConstructorMatch )
{

   int		i;
   int		end;
   char*    pText = NULL;
   gchar*	pLine = NULL;			/* holds extracted line */
   int		idxLineStart;			/* index to start of current line in text widget */
   int		idxLineEndTentative;	/* likely will index past the end, TBD */
   int		found = FALSE;
   int      matchLen = 0;

   gsize   textLen=0;
   pText = ActiveTextWidgetContents( &textLen );
   if ( pText == NULL ) return;
  /*
   * Extract enough from the text widget to include the current line.
   * The end index (which is guessed) must be chosen so that it is not beyond
   * the end of the text widget.
   */
//   idxLineStart        = gtk_text_get_point( GTK_TEXT(pWidgetTextActive) );
   int LineSaved = LineAtCursor();
   idxLineStart = OffsetAtCursor(); // utf8 index
   gchar* pTextOffset = g_utf8_offset_to_pointer(pText, idxLineStart);

   // scroll to start of line
   while ( pTextOffset != pText && !IsEol(pTextOffset))
   {
           //g_print("%s: %c\n","11",*pTextOffset);
           --pTextOffset;
   }
   if ( pTextOffset != pText )
      ++pTextOffset;
   
   idxLineStart = (int)(pTextOffset - pText) ;   //ASCII index
   
   idxLineEndTentative = idxLineStart + MAX_LINE_LEN;
   if ( idxLineEndTentative > textLen )
      idxLineEndTentative =  textLen ;

   
/*// index to end of line
   idxLineEndTentative = idxLineStart;
   while ( !IsEol(&pText[idxLineEndTentative]) &&
           pText[idxLineEndTentative] != '\0')
   {
       idxLineEndTentative ++;
       //g_print("%s: %c\n","22",pText[idxLineEndTentative-1]);
   }
   idxLineEndTentative --;
 */
  
   pLine = &pText[idxLineStart];
   g_return_if_fail(pLine == pTextOffset);

  /* 
   * Find the index of the first matching word in the line.
   * The loop ends when the last char of the sought word
   * would hit the tentative end of the line.
   */
   end = idxLineEndTentative - idxLineStart - strlen( pWord );
   //g_print("idxLineStart=%d idxLineEndTentative=%d end=%d",idxLineStart,idxLineEndTentative,end);
   
   if ( end < 0 || end > MAX_LINE_LEN ) return;		/* oops, bail out */

   if ( searchMethod == SEARCH_NORMAL )
   {
      matchLen = strlen( pWord );
      for ( i = 0; i <= end; ++i )
      {
         if ( Strncmpx( &pLine[i], pWord, matchLen, caseSens ) == 0 )
         {
           /*
            * To highlight the function name rather than the class name in C++ constructors:
            * Foo::Foo()
            * Foo<T>::Foo()
            */
            if ( tryConstructorMatch )
            {
               int j;
               for ( j = i+matchLen; j <= end; ++j )
               {
                  if ( pLine[j] == ':'  &&  pLine[j+1] == ':' )
                  {
                     if ( Strncmpx( &pLine[j+2], pWord, matchLen, caseSens ) == 0 )
                        i = j+2;    /* found second match, move index to it */
                     break;         /* break match or no match */
                  }
                  else if ( pLine[j] == '\n' )
                     break;
               }
            }
            found = TRUE;
            break;
         }
      }
   }
   else /* regex */
   {
      char* pMatch;
      pMatch = FindRegexSegment( &matchLen,
                                 pLine,
                                 idxLineEndTentative - idxLineStart,
                                 pWord,
                                 caseSens,
                                 FALSE, /* forward */
                                 REG_NEWLINE );
      if ( pMatch )
      {
         found = TRUE;
         i = pMatch - pLine;
         if ( i < 0 || i > MAX_LINE_LEN )
            found = FALSE; /* failsafe */         
      }
      else
      {
         found = i = FALSE;
      }
   }

  /*
   * Highlight the first match (if found).
   */
   if ( found )
   {
      glong begin = g_utf8_pointer_to_offset(pText, pText+idxLineStart+i);
      glong end2   = g_utf8_pointer_to_offset(pText, pText+idxLineStart+i+matchLen);
      
      SelectRegion( gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive)),
                    begin,
                    end2);
                    //idxLineStart + i,
                    //idxLineStart + i + matchLen );
      return;
   }

  /*
   * No match.  As a contigency plan, highlight the entire line.
   */
   HighlightLine(LineSaved);


   return;
}

/*****************************************************************************
 * Highlight a string in the text widget.
 * The text widget will be scrolled to show the string.
 * @param   pText
 *          Pointer to the first char of the text of the text widget.
 * @param   idx
 *          Index of the string to highlight.
 * @param   len
 *          Length of string to highlight.
 *****************************************************************************/
void
HighlightTextString( gchar* pText,
                     gint   idx, // ASCII index
                     gint   len )// ASCII index
{
   g_return_if_fail( pText );
   g_return_if_fail( idx >= 0  &&  idx <= MAX_LINE_LEN * MAX_LINES );
   g_return_if_fail( len > 0 /*&&  len <= MAX_LINE_LEN*/ );
                             /*^^ disabled to allow multi-line regex matches */
  /*
   * Scroll the text widget to the string.
   */
   /* SelectRegion will do this for you
   ScrollTextWidgetToLine( LineIdx2LineNum( pText, idx ),
                           SCROLL_TEXT_VERT_FRAC );
    */
   // from ASCII index to UTF8 index
   glong newIdx = g_utf8_pointer_to_offset(pText, pText+idx);

   glong newIdx2= g_utf8_pointer_to_offset(pText, pText+idx+len);

   
#ifdef HYPERSRC_DEBUG
   glong newIdx_1 = AsciiIndexToUTF8Index(pText, idx);    
   glong newIdx2_1 = AsciiIndexToUTF8Index(pText, idx+len);
   g_assert(newIdx_1 == newIdx);
   g_assert(newIdx2_1 == newIdx2);
#endif
   
   g_return_if_fail(newIdx2 < MAX_LINE_LEN * MAX_LINES);

  /*
   * Highlight the string.
   */
   GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
   SelectRegion( (pBuf),
                 newIdx,
                 newIdx2);  // - newIdx );
   return;
}


/*****************************************************************************
 * Highlight the tag in the text widget.
 * @param   pTag
 * @param   idxCursor
 *          For restoring a recorded cursor position (from history).
 *          Pass -1 if not restoring.
 *****************************************************************************/

void
HighlightTag( tag_t* pTag,
              guint  idxCursor )//ASCII index
{
  /*
   * Highlight the tag in the text widget.
   * ctags inserts a space between "operatorX",
   * so to ensure the highlight search works, just specify "operator".
   */
   if ( ! strstr( pTag->pName, "operator" ) )
   {
      if ( ! strstr( pTag->pName, "(recorded position)" ) )
      {
         if ( idxCursor != -1 )
            HighlightChar( pTag->lineInt, idxCursor );
         else
            HighlightWordInCurrentLine( pTag->pName, TRUE, SEARCH_NORMAL, TRUE );
      }
      else
      {
         if ( idxCursor != -1 )
            HighlightChar( pTag->lineInt, idxCursor );
         else
            HighlightLine( pTag->lineInt );
      }
   }
   else
   {
      HighlightWordInCurrentLine( "operator", TRUE, SEARCH_NORMAL, TRUE );
   }
   return;
}

/*****************************************************************************
 * Highlight a char in a text widget.
 * @param   line
 *          Line # that contains the char (for scrolling).
 * @param   idxCursor
 *          Index of char (from start of contents of text widget).
 *****************************************************************************/
void
HighlightChar( int line,
               int idxText )//ASCII index
{
/* SelectRegion do this for you
   ScrollTextWidgetToLine( line,
                           SCROLL_TEXT_VERT_FRAC );
 */
   GtkTextBuffer *pBuf = gtk_text_view_get_buffer(
                           GTK_TEXT_VIEW(pWidgetTextActive));

   //convert ASCII idxText to UTF8 idxText2
   //GtkTextIter start, end;
   //gtk_text_buffer_get_bounds(pBuf, &start, &end);
   //gchar * pText = gtk_text_buffer_get_text(pBuf, &start, &end, FALSE);
   guint unused=0;
   gchar* pText= ActiveTextWidgetContents(&unused);
   if ( idxText > unused )
       return; // out of range
   gint idxText2 = g_utf8_pointer_to_offset(pText, pText + idxText); 
   
   //g_free(pText);
   
   SelectRegion( pBuf,
                 idxText2,
                 idxText2 + 1 );
   return;
}

/*
 * @return The active text view's current line's offset
 */
gint
OffsetAtLine()
{/*
	g_return_val_if_fail( !IsTextWidgetEmpty(), -1);
	gint offset = OffsetAtCursor();//LineAtCursor();
	if ( line < 0 )
		return -1;
	GtkTextIter iter;
	
	if( line = 0 )
	    line = 1;
	gtk_text_buffer_get_iter_at_line(gtk_text_view_get_buffer(
						GTK_TEXT_VIEW(pWidgetTextActive)), &iter, line -1);
 */
	return 0;//gtk_text_iter_get_line_offset(&iter) +1;
}

/*****************************************************************************
 * Print the line # in the statusbar.
 * @return The line # (starting with 1) or -1 if text widget is empty.
 *****************************************************************************/
int
PrintLineNum( void )
{
   char *msg;
   int	line;

  /*
   * Skip if text widget is empty.
   */
   if ( IsTextWidgetEmpty() ) return -1;

  /*
   * Print the line # in the statusbar.
   */
   line = LineAtCursor();
   
   /* !!! Change PrintStatusbar() if the below parm #2 is changed !!! */
   msg = g_strdup_printf(" line: %d row: %d", line, OffsetAtLine() ); 
   PrintStatusbar( msg );
	
   g_free(msg);
   return line;
}

/*****************************************************************************
 * Prints the line # in the statusbar, and updates the functree.
 * Called by GTK+ when GTK+ is idle, then shuts off.
 * Does nothing if the text widget is empty.
 *****************************************************************************/
gint
IdleLineNum( gpointer dontCare )
{
  int	line;

  /*
   * Returning zero causes GTK+ to stop calling this idle func.
   *
   * Is the text widget empty?
   */
   if ( IsTextWidgetEmpty() ) return FALSE;

   line = PrintLineNum();

  /*
   * Update functree if necessary.
   */
   if ( !IsAnyMajorFunctionBusy(0)  &&  !IsAnyTreeFunctionBusy() )
      UpdateFuncTree( line, FALSE );

   return FALSE;  /* FALSE tells GTK+ to stop calling an idle callback */
}

/*****************************************************************************
 * @fn void SelectRegion( GtkText* pGtkText, int idxStart, int idxEnd );
 * Select a region in a text widget.
 *****************************************************************************/
static GtkTextBuffer*	 selectRegion_pGtkTextBuffer;
static int		 selectRegion_idxStart;
static int		 selectRegion_idxEnd;

void
SelectRegion( GtkTextBuffer *pTextBuffer,
              int      idxStart,  // UTF-8 index
              int      idxEnd )   // UTF-8 index
{
g_return_if_fail(     pTextBuffer);
g_return_if_fail(  idxStart >= 0);
g_return_if_fail(  idxEnd   > idxStart);
g_return_if_fail( idxEnd   < MAX_TEXT_BUFFER_LEN);
g_return_if_fail(  idxEnd   < ActiveTextWidgetSize() );

   selectRegion_pGtkTextBuffer	= pTextBuffer;
   selectRegion_idxStart	= idxStart;
   selectRegion_idxEnd		= idxEnd;

  /*
   * KLUDGE:
   * The highlight on the selection often will disappear if
   * gtk_editable_select_region() is called immediately after
   * gtk_editable_set_position().  Processing events in between
   * the two ensures the highlight stays, but for stability,
   * use an idle function rather than ProcessPendingEvents().
   */
#if 1
//   gtk_editable_set_position( GTK_EDITABLE( GTK_WIDGET(pGtkText) ),
//                              idxStart );
    if ( idxStart == 0 ) idxStart = 1;
	GtkTextIter iter;
	gtk_text_buffer_get_iter_at_offset(pTextBuffer,
									   &iter,
									   idxStart -1);
	// scroll to the start offset
	ScrollTextWidgetToLine(gtk_text_iter_get_line(&iter)+1, 
	                       SCROLL_TEXT_VERT_FRAC);
#endif

   gtk_idle_add( IdleSelectRegion, NULL );

   return;
}
/** See SelectRegion() */
gint
IdleSelectRegion( gpointer unused )
{
	GtkTextIter StartIter, EndIter;
	GtkTextBuffer *pBuf=gtk_text_view_get_buffer(
						GTK_TEXT_VIEW(pWidgetTextActive));
    //gtk_text_buffer_get_iter_at_index // in byte
	gtk_text_buffer_get_iter_at_offset(pBuf,
									   &StartIter, 
									   selectRegion_idxStart);
	gtk_text_buffer_get_iter_at_offset(pBuf,
									   &EndIter,
									   selectRegion_idxEnd);
	//
//	gtk_text_iter_set_line_offset(&StartIter, 0);//start of line
//	gtk_text_iter_forward_to_line_end(&EndIter); //end  of line
	gtk_text_buffer_select_range(pBuf,
								 &StartIter,
								 &EndIter);
   return FALSE;  /* FALSE tells GTK+ to stop calling an idle callback */
}

#if 0 // use OffsetAtCursor() instead
/*****************************************************************************
 * Return index of cursor of text widget.
 * Caller should ensure text widget is not empty!
 *****************************************************************************/
guint
TextCursorIdx( void )
{
g_assert( ! IsTextWidgetEmpty() );
	return OffsetAtCursor();
}
#endif

/******************************************************************************
 * 注意： GtkTextBuffer 中存放的是UTF-8编码的文本
 * g_utf8_pointer_to_offset() g_utf8_offset_to_pointer() g_utf8_get_char()
 * @return True if cursor is over a word in the text widget.
 ******************************************************************************/
gboolean
IsCursorOverWord( void )
{

   if ( IsTextWidgetEmpty() ) return FALSE;
#if 0 //lv
  /*
   * Obtain pointer to contents of text widget.
   */
   pText = ActiveTextWidgetContents( NULL );
   if ( pText == NULL ) return FALSE;

  /*
   * Return whether cursor is over a char which is/isnt part of a word.
   */
   c = pText[ gtk_editable_get_position( GTK_EDITABLE(pWidgetTextActive)) ];
#endif
   GtkTextIter iter;
   GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
   gtk_text_buffer_get_iter_at_offset(pBuf, &iter, OffsetAtCursor());
   gunichar uni_char=gtk_text_iter_get_char(&iter);
   if ( 0xFFFC == uni_char || 0 == uni_char ) // unknown character
	   return FALSE;
   return IsWordChar( uni_char );
}

/*****************************************************************************
 * @return True if a char is part of a word.
 * Depends on the type of source file being browsed.
 *****************************************************************************/
gboolean
IsWordChar( gunichar c )
{
  /*
   * Which kind of source file is being browsed?
   * Eg, "$temp" is considered a word in Perl but not in C.
   */
   char* pModuleName = ActiveModuleName();
   if ( pModuleName != NULL )
   {
      if ( IsSrcFileC( pModuleName ) )           /* pure C */
         return IsCIdChar( c );
      else if ( IsSrcFileCish( pModuleName ) )   /* C++ or Java */
         return IsCishIdChar( c );
      else if ( IsSrcFilePerl( pModuleName ) )   /* Perl */
         return isalnum(c) || c == '_' || IsPerlVarChar(c);
      else if ( IsSrcFileForth( pModuleName ) )  /* FORTH */
         return ! IsWhitespace(c);
   }
   return ! IsWhitespace(c);                     /* default */
}


/*****************************************************************************
 * Scoop the word which the cursor is over (in text widget).
 * @param   maxWordLen
 *          Returns failure if word is larger.
 * @param   pText
 *          Pointer to word extracted from text widget.
 * @param   textLen
 *          Length of word extracted from text widget.
 * @param   idxCursor
 *          Index of cursor
 * @return  Pointer to a GString, or NULL.
 *****************************************************************************/
GString*
ScoopWordAtCursor( int   maxWordLen,
                   char* pText,
                   int   textLen,
                   int   idxCursor )
{
   GString* pGstr	= NULL;
   int		idxWordStart;
   int		idxWordEnd;
   int		i;

   g_return_val_if_fail(  pText
             && textLen > 0
             && textLen < MAX_TEXT_BUFFER_LEN
             && idxCursor >= 0
             && idxCursor <= textLen ,NULL);		/* GTK lets cursor idx equal text len */

   idxWordStart = idxCursor;

   if ( IsTextWidgetEmpty() ) return NULL;

  /*
   * Is the cursor is over a non-word char (ie whitespace)?
   */
   if ( ! IsWordChar( GetUnicodeChar(pText, idxCursor ) ))
      return NULL;

  /*
   * Compute idxWordStart.
   *
   * If the cursor looks like it is over the middle of a word,
   * then scan backwards for the start of the word.
   */
   for ( i = 0; i < maxWordLen; ++i )
   {
     /*
      * To next preceding char.
      */
      --idxWordStart;

     /*
      * Break if encountered the first preceding non-word char,
      * or beginning of text widget.
      */
      if (    (idxWordStart < 0)
           || (! IsWordChar( GetUnicodeChar(pText, idxWordStart ))))
      {
        /*
         * Move index forward to start of word, break.
         */
         ++idxWordStart;
         break;
      }

     /*
      * Index is still at a word char.
      * Break if the index is now 0 (word is at beginning of text widget).
      */
      if ( idxWordStart == 0 ) break;
   }
   if ( i >= maxWordLen ) return NULL;

  /*
   * Compute idxWordEnd.
   */
   idxWordEnd = idxWordStart + 1; //=idxCursor + 1 ?
   for ( i = 0; i < maxWordLen; ++i )
   {
     /*
      * Break if end of text reached.
      */
      if ( idxWordEnd >= textLen )
      {
         idxWordEnd = textLen - 1;
         break;
      }

     /*
      * Scan until the first non-word char that trails the word.
      */
      if ( ! IsWordChar( GetUnicodeChar(pText, idxWordEnd)))
      {
         --idxWordEnd;
         break;
      }

     /*
      * Forward to next char.
      */
      ++idxWordEnd;
   }
   if ( i >= maxWordLen ) return NULL;

  /*
   * Abort if the indexes were somehow miscomputed.
   */
   g_assert(    idxWordEnd >= idxWordStart
             && (idxWordEnd - idxWordStart + 1) > 0
             && (idxWordEnd - idxWordStart + 1) <= maxWordLen );

  /*
   * Copy the word into a GString.
   */
//   pGstr = Segment2GString( &pText[idxWordStart],
//                            &pText[idxWordEnd] - &pText[idxWordStart] + 1 );

   pGstr = Segment2GString(g_utf8_offset_to_pointer(pText, idxWordStart),
						   idxWordEnd - idxWordStart +1 );
  /*
   * Return GString.
   */
   return pGstr;
}

/*****************************************************************************
 * Record the current position in the text widget.
 * @param   pCursor (OUT)
 *          Pointer to a struct that defines state of the cursor position.
 *****************************************************************************/
void
SaveCursor( cursor_t* pCursor )
{
  /*
   * If text widget is empty then invalidate struct, silently return.
   */
   if ( IsTextWidgetEmpty()  ||  !pWidgetTextActive )
   {
      pCursor->valid = FALSE;
      return;
   }

  /*
   * Fill the passed cursor_t struct.
   */
   pCursor->valid           = TRUE;
   pCursor->pWidgetText     = pWidgetTextActive;
   pCursor->adjustmentValue = 0;//GTK_ADJUSTMENT(GTK_TEXT(pWidgetTextActive)->vadj)->value;
   pCursor->idxCursor       = OffsetAtCursor();//TextCursorIdx();
   strncpy( pCursor->module, ActiveModuleName(), MAX_FILENAME_LEN );

   return;
}

/*****************************************************************************
 * Restore the current position in the text widget.
 * @param   pCursor (IN)
 *          Pointer to a struct that defines state of the cursor position.
 *****************************************************************************/
void
RestoreCursor( cursor_t* pCursor )
{
  /*
   * Quit if cursor_t struct is not valid
   * (if so, text widget was empty when SaveCursor() was called)
   */
   if ( ! pCursor->valid )
      return;

  /*
   * Did user switch to the other text widget?
   * If so, then return, because the state in cursor_t is N/A.
   */
   if ( pCursor->pWidgetText != pWidgetTextActive )
      return;

  /*
   * This func will only load a module if the text widget
   * is not already holding it.
   */
   LoadTextWidgetWithModule( pCursor->module,
                             0,
                             ROW_NONE,
                             DONT_RELOAD );
#if 0 // cancel adjustment manually
  /*
   * Restore the cursor position and the state of the text widget.
   */
   SetAdjustmentValue( GTK_ADJUSTMENT(GTK_TEXT(pWidgetTextActive)->vadj),
                       pCursor->adjustmentValue );
 //  gtk_editable_set_position( GTK_EDITABLE(pWidgetTextActive),
 //                             pCursor->idxCursor );
#endif

   //ScrollToCursor(GTK_TEXT_VIEW(pWidgetTextActive), pCursor->idxCursor);
   GtkTextIter iter;
   gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(GTK_TEXT_VIEW(pCursor->pWidgetText)),
                                      &iter, 
                                      pCursor->idxCursor==0 ? 1 : (pCursor->idxCursor-1));
   gint WhichLine = gtk_text_iter_get_line(&iter)+1;
   ScrollTextWidgetToLine(WhichLine, SCROLL_TEXT_VERT_FRAC);
   return;
}

/*****************************************************************************
 * Return a pointer to active text widget's contents.
 * !!! DO NOT TRY TO FREE THE CONTENTS USING G_MALLOC() !!!
 * @param   pSizeOut (OUT)
 *          Pass NULL if size isn't wanted.
 * @return  NULL if failure (unexpected).
 *****************************************************************************/
char*
ActiveTextWidgetContents( guint* pSizeOut )
{
   char*	pBuf;
   guint	bufSize;

   if ( pWidgetTextActive == NULL ) return NULL;
   if ( pWidgetTextActive == pWidgetTextAux )
        goto ret;

   GtkTextIter start, end;
   GtkTextBuffer *pTextBuf = gtk_text_view_get_buffer(
                     GTK_TEXT_VIEW(pWidgetTextActive));
   if ( ! gtk_text_buffer_get_modified(pTextBuf))
       goto ret;
//   gtk_text_buffer_get_bounds(pTextBuf, &start, &end);
   gtk_text_buffer_get_start_iter(pTextBuf, &start);
   gtk_text_buffer_get_end_iter(pTextBuf, &end);
   pBuf = gtk_text_buffer_get_text(pTextBuf, &start, &end, FALSE); //ignore hidden chars
 
   if ( !g_utf8_validate(pBuf, -1 , NULL) )
       g_print("从 TextBuffer 返回的文本不是utf8编码\n");
 
   module_t * pActModule = ActiveModule();
   if ( pActModule )
   {
       g_free(pActModule->pModuleBuffer);
       pActModule->pModuleBuffer = g_strdup(pBuf); // must do this
       pActModule->moduleSize = strlen ( pActModule->pModuleBuffer );
       g_free(pBuf);
   }
   else
   {
       g_free(pBuf);
   }
ret:
   pBuf = ModuleBuffer( ActiveModuleName(), &bufSize );

#ifdef HYPERSRC_DEBUG
   g_print("***new_buf_len: %d\n",bufSize);
#endif

  /*
   * Return size if requested.
   */
   if ( pSizeOut )
   {
      if ( pBuf )
         *pSizeOut = bufSize;
      else
         *pSizeOut = 0; /* oops, ModuleBuffer() failed */
   }
      
   return pBuf;
}

/*****************************************************************************
 * @return Size of module loaded in active text widget.
 *         ASCII strlen, not conside about unicode
 *****************************************************************************/
guint
ActiveTextWidgetSize( void )
{
   guint bufSize = 0;
   if ( pWidgetTextActive  &&  !IsStringEmpty( ActiveModuleName() ) 
        && ActiveModuleName() )
   {
#ifdef HYPERSRC_DEBUG

      //guint lenText = gtk_text_get_length( GTK_TEXT(pWidgetTextActive) );
      ModuleBuffer( ActiveModuleName(), &bufSize );
      //g_return_val_if_fail( pBuf  &&  lenText == bufSize, lenText );
#else
      ModuleBuffer( ActiveModuleName(), &bufSize );
#endif
#if 0 // 这个实现有问题
	  // 要考虑是否 unicode
	  GtkTextIter iter;
	  GtkTextBuffer *pBuf=gtk_text_view_get_buffer(GTK_TEXT_VIEW(pWidgetTextActive));
	  gtk_text_buffer_get_end_iter(pBuf, &iter);
	  return gtk_text_iter_get_offset(&iter);//多字节算一个，回车算一个
//      return gtk_text_get_length( GTK_TEXT(pWidgetTextActive) );
#endif
	  return bufSize;
   }

   return 0;
}

/*****************************************************************************
 * Set the active text widget (the one that will be hyperjumped into).
 * Pass pWidgetTextMain[Aux].
 * This doesn't affect focus (which GiveFocusToTextWidget() is meant for).
 *****************************************************************************/
void
SetActiveTextWidget( GtkWidget* pWidget )
{
g_return_if_fail( pWidget );

   if ( pWidget == pWidgetTextMain )
   {
      pWidgetTextActive   = pWidgetTextMain;
      pFocusbarTextActive = pFocusbarTextMain;
   }
   else if ( pWidget == pWidgetTextAux )
   {
      pWidgetTextActive   = pWidgetTextAux;
      pFocusbarTextActive = pFocusbarTextAux;
   }
   else
      g_return_if_fail(0);
}

/*****************************************************************************
 * Give the focus to a text widget.
 * Pass pWidgetTextMain[Aux].
 *****************************************************************************/
void
GiveFocusToTextWidget( GtkWidget* pWidget )
{
g_return_if_fail( pWidget );

  /*
   * Avoid endless cycle with HandlerFocusIn() which calls this function!
   */
   if ( pWidget == pWidgetTextMain )
   {
      if ( pWidgetWithFocus != pWidgetTextMain )
         gtk_widget_grab_focus( pWidgetTextMain );
   }
   else if ( pWidget == pWidgetTextAux )
   {
      if ( pWidgetWithFocus != pWidgetTextAux )
         gtk_widget_grab_focus( pWidgetTextAux );
   }
   else
      g_return_if_fail(0);
}

/*****************************************************************************
 * Switch the active text widget if the aux text key is pressed.
 * @param   howtoSwitch
 *          Pass DONT_SWITCH_UNLESS_AUX_TEXT_KEY or SWITCH_PER_AUX_TEXT_KEY.
 *****************************************************************************/
void
SwitchActiveTextWidget( int howtoSwitch )
{
  /*
   * Disregard passed parameter unless user has set a preference
   * (if so, then textWidgetBehavior != SWITCH_PER_AUX_TEXT_KEY)
   * to hyperjump in text widget that has the focus.
   */
   if (    textWidgetBehavior == SWITCH_PER_AUX_TEXT_KEY
        || howtoSwitch        == SWITCH_PER_AUX_TEXT_KEY )
   {
     g_assert(0); //此分支不再使用
     /*
      * Disregard focus, activate either text widget based on whether
      * the aux text key is pressed or released.
      * This makes sense when the user has the cursor in one text widget
      * but wants to hyperjump in the other widget by pressing F5 or Shift+F5.
      */
//      SetActiveTextWidget( !auxKeyPressed ? pWidgetTextMain : pWidgetTextAux );
      /*
       * 按住 Shift 键后，则切换到另一个 TextView
       */

      if ( auxKeyPressed )
      {
         if ( pWidgetTextActive == pWidgetTextMain )
             SetActiveTextWidget( pWidgetTextAux);
		 else
             SetActiveTextWidget( pWidgetTextMain);
         auxKeyPressed = FALSE;
       }
   }
   else if ( howtoSwitch == DONT_SWITCH_UNLESS_AUX_TEXT_KEY )
   {
     /*
      * Let whichever text widget has the focus remain activated,
      * unless the aux text key is pressed by user.
      * Eg, this makes sense when the user clicks one of the text widgets
      * and uses Find/Module, and wants the module to be loaded into
      * text widget that was clicked (that has the focus).
      */
//      if ( auxKeyPressed ) SetActiveTextWidget( pWidgetTextAux );
      /*
       * 按住 Shift 键后，则切换到另一个 TextView
       */

      if ( auxKeyPressed )
      {
         if ( pWidgetTextActive == pWidgetTextMain )
             SetActiveTextWidget( pWidgetTextAux);
		 else
             SetActiveTextWidget( pWidgetTextMain);
         auxKeyPressed = FALSE;
       }
   }
   else
   {
      g_return_if_fail(0);
   }
}

/*****************************************************************************
 * Functions for assigning/obtaining the name of the module loaded
 * in either the main or aux text widget.
 *****************************************************************************/

static gchar moduleInTextMain[MAX_FILENAME_LEN] = "";	/* shouldn't be used elsewhere */
static gchar moduleInTextAux[MAX_FILENAME_LEN]  = "";

void
RecordActiveModuleName( char* pModuleName )
{
   if ( pWidgetTextActive == pWidgetTextMain )
   {
      strncpy( moduleInTextMain, pModuleName, MAX_FILENAME_LEN  );
   }
   else if ( pWidgetTextActive == pWidgetTextAux )
   {
      strncpy( moduleInTextAux, pModuleName, MAX_FILENAME_LEN );
   }
   else g_assert(0);
}
char*
ActiveModuleName( void )
{
   if ( pWidgetTextActive == pWidgetTextMain )
   {
      return moduleInTextMain;
   }
   else if ( pWidgetTextActive == pWidgetTextAux )
   {
      return moduleInTextAux;
   }
   else { g_assert(0); return NULL; }
}

/*****************************************************************************
 * @fn module_t* ActiveModule( void );
 * Functions for recording/obtaining information about the module displayed
 * in the main/aux text widget.
 *****************************************************************************/

static module_t* pModuleTextMain;	/* shouldn't be used elsewhere */
static module_t* pModuleTextAux;

void
RecordActiveModule( module_t* pModule ) /* NULL is allowed */
{
   if ( pWidgetTextActive == pWidgetTextMain )
   {
      pModuleTextMain = pModule;
   }
   else if ( pWidgetTextActive == pWidgetTextAux )
   {
      pModuleTextAux = pModule;
   }
   else g_assert(0);
}
module_t*
ActiveModule( void )
{
   if ( pWidgetTextActive == pWidgetTextMain )
   {
      return pModuleTextMain;
   }
   else if ( pWidgetTextActive == pWidgetTextAux )
   {
      return pModuleTextAux;
   }
   else { g_assert(0); return NULL; }
}

module_t*
GetModuleTextMain( void )
{
    return pModuleTextMain;
}

void
SetModuleTextMain( module_t *pModule )
{
    pModuleTextMain = pModule;
}

module_t*
GetModuleTextAux( void )
{
    return pModuleTextAux;
}

void
SetModuleTextAux ( module_t *pModule )
{
    pModuleTextAux = pModule;
    
    /*
     * record the AUX's module name
     */
    if ( NULL != pModule )
        strncpy( moduleInTextAux, pModule->pModuleName, MAX_FILENAME_LEN );
    else
        moduleInTextAux[0] = '\0';
}

GtkWidget*
GetWidgetTextMain( void )
{
    return pWidgetTextMain;
}

GtkWidget*
GetWidgetTextAux( void)
{
    return pWidgetTextAux;
}

/*
 * Set the buffer highlight
 * @return
 *           TRUE if successful.
 */
gboolean
SetBufferHighlight(GtkSourceBuffer *buffer, const gchar * filename)
{
	gchar *			  mime_type =NULL;
	gchar *           uri       =NULL;
	GtkSourceLanguage * language=NULL;
	GtkSourceLanguageManager *manager = NULL;

	g_return_val_if_fail(NULL != buffer, FALSE);
	g_return_val_if_fail(NULL != filename, FALSE);
	
	// set language highlight

	if ( g_path_is_absolute(filename) )
	{
		uri = gnome_vfs_get_uri_from_local_path(filename);
	}
	else
	{
		gchar *curdir, *path;
		curdir = g_get_current_dir();
		path   = g_strconcat(curdir,"/", filename, NULL);
		g_free(curdir);
		uri = gnome_vfs_get_uri_from_local_path(path);
		g_free(path);
	}
	mime_type = gnome_vfs_get_mime_type(uri);
	g_free(uri);
	
	if ( mime_type )
	{
		manager = g_object_get_data(G_OBJECT(buffer), "languages-manager");
		g_return_val_if_fail(manager, FALSE);

		language = gtk_source_language_manager_guess_language(
								manager,
								filename,
								mime_type);
		if ( NULL == language )
		{
			g_print("No language found for mime-type: %s\n", mime_type);
			g_object_set(G_OBJECT(buffer),"highlight", FALSE, NULL);
			return FALSE;
		}
		else
		{
			g_object_set(G_OBJECT(buffer), "highlight", TRUE, NULL);
			gtk_source_buffer_set_language( buffer, language);
		}
		g_free(mime_type);
	}
	return TRUE;
}

/*************************************************************************
 * @return 
 *      TRUE if pWidgetTextMain loaded a file and have been modified
 *           or have no file load but pWidgetTextMain have contents.
 *************************************************************************/
gboolean
IsMainTextViewHaveContents(void)
{
    GtkTextIter first;
    GtkWidget * pTextView = GetWidgetTextMain();
    if ( NULL == pTextView )
        return FALSE;
    
    GtkTextBuffer * pTextBuf = gtk_text_view_get_buffer(
                                GTK_TEXT_VIEW(pTextView));
    gtk_text_buffer_get_start_iter(pTextBuf, &first);
    
    gint bytes = gtk_text_iter_get_bytes_in_line(&first);
    return 0 != bytes;
}

/*************************************************************************
 * @return 
 *      TRUE if pWidgetTextMain loaded a file and have been modified
 *************************************************************************/
gboolean
IsMainModuleModified(void)
{
	GtkWidget * pWidgetTextMainTmp = GetWidgetTextMain();
	if ( ! pWidgetTextMainTmp )
	    return FALSE;

	module_t * pModule = GetModuleTextMain();//ActiveModule();
	if ( ! pModule )
		return FALSE;
		
	GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
							pWidgetTextMainTmp));//pWidgetTextActive));
	gboolean modified = gtk_text_buffer_get_modified(pBuf); 

	return modified;
}

/*
 * 设置当前可用的 TextView 的修改属性
 */
void
SetMainModuleModified(gboolean IsModified)
{
//	module_t * pModule = ActiveModule();
	module_t * pModule = GetModuleTextMain();//pModuleTextMain;
    GtkWidget * pWidgetTextMainTmp = GetWidgetTextMain();
    if ( ! pWidgetTextMainTmp )
        return ;
	GtkTextBuffer *pBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
							pWidgetTextMainTmp));//pWidgetTextActive));
 
	if ( ! pModule )
	{
	   DebugMsg("pModuleTextMain is NULL");
	   return;
		//g_assert(pModule->modified == modified);
	}

	if ( IsModified )
	{
		gtk_text_buffer_set_modified(pBuf, TRUE);
		if ( pModule )
			pModule->modified = TRUE;
	}
	else
	{
		gtk_text_buffer_set_modified(pBuf, FALSE);
		if ( pModule )
			pModule->modified = FALSE;
	}
	return;
}

